﻿using DimensionsHelper.Common.Contracts.Excel;

namespace DimensionsHelper.Client.WinUi.Controls;

[DebuggerDisplay("{ToString(),nq}")]
public partial class BorderGridRange : FrameworkElement, IList<BorderGridRange>
{
    internal const int X1 = 0;
    internal const int Y1 = 1;
    internal const int X2 = 2;
    internal const int Y2 = 3;

    private static readonly object Lock = new();

    private static readonly HashSet<BorderGridRange> IsHiddenNeedToSet = [];
    private static readonly HashSet<BorderGridRange> IsMergedNeedToSet = [];


    public static readonly DependencyProperty AddHorizontalBorderProperty = DependencyProperty.Register(
        nameof(AddHorizontalBorder),
        typeof(bool),
        typeof(BorderGridRange),
        new PropertyMetadata(false, OnAddHorizontalBorderPropertyChanged));


    public static readonly DependencyProperty AddVerticalBorderProperty = DependencyProperty.Register(
        nameof(AddVerticalBorder),
        typeof(bool),
        typeof(BorderGridRange),
        new PropertyMetadata(false, OnAddVerticalBorderPropertyChanged));


    public static readonly DependencyProperty AddAroundBorderProperty = DependencyProperty.Register(
        nameof(AddAroundBorder),
        typeof(bool),
        typeof(BorderGridRange),
        new PropertyMetadata(false, OnAddAroundBorderPropertyChanged));


    public static readonly DependencyProperty BorderPositionProperty = DependencyProperty.Register(
        nameof(BorderPosition),
        typeof(BorderPosition),
        typeof(BorderGridRange),
        new PropertyMetadata(BorderStyle.None));


    public static readonly DependencyProperty BorderStyleProperty = DependencyProperty.Register(
        nameof(BorderStyle),
        typeof(BorderStyle),
        typeof(BorderGridRange),
        new PropertyMetadata(BorderStyle.None, OnBorderStylePropertyChanged));


    public static readonly DependencyProperty BorderBrushProperty = DependencyProperty.Register(
        nameof(BorderBrush),
        typeof(Brush),
        typeof(BorderGridRange),
        new PropertyMetadata(new SolidColorBrush(Colors.Transparent)));


    public static readonly DependencyProperty BorderColorProperty = DependencyProperty.Register(
        nameof(BorderColor),
        typeof(Color),
        typeof(BorderGridRange),
        new PropertyMetadata(Colors.Black, OnBorderColorPropertyChanged));


    public static readonly DependencyProperty IsMergedProperty = DependencyProperty.Register(
        nameof(IsMerged),
        typeof(bool),
        typeof(BorderGridRange),
        new PropertyMetadata(false, OnIsMergedPropertyChanged));


    public static readonly DependencyProperty IsHiddenProperty = DependencyProperty.Register(
        nameof(IsHidden),
        typeof(bool),
        typeof(BorderGridRange),
        new PropertyMetadata(false, OnIsHiddenPropertyChanged));

    private readonly int[] _visibleBounds = new int[4];

    public BorderGridRange()
    {
        Row = 0;
        RowSpan = 1;
        Column = 0;
        ColumnSpan = 1;

        _visibleBounds[X1] = 0;
        _visibleBounds[X2] = 1;
        _visibleBounds[Y1] = 0;
        _visibleBounds[Y2] = 1;
    }


    public bool IsInitialized => OwnerGrid?.IsLoaded == true && OwnerGrid.Ranges.IsInitialized;


    public BorderGridRange? ContainingRange
    {
        get;
        internal set;
    }


    public int Level
    {
        get
        {
            if (ContainingRange == null)
            {
                return 0;
            }

            return ContainingRange.Level + (IsBorderVisible ? 1 : 0);
        }
    }


    public int Row
    {
        get => field;
        set
        {
            field = value;
            _visibleBounds[Y1] = value;
            _visibleBounds[Y2] = value + RowSpan - 1;
        }
    }


    public int RowSpan
    {
        get => field;
        set
        {
            field = value;
            _visibleBounds[Y2] = Row + value - 1;
        }
    }


    public int Column
    {
        get => field;
        set
        {
            field = value;
            _visibleBounds[X1] = value;
            _visibleBounds[X2] = value + ColumnSpan - 1;
        }
    }


    public int ColumnSpan
    {
        get => field;
        set
        {
            field = value;
            _visibleBounds[X2] = Column + value - 1;
        }
    }


    internal BorderGrid? OwnerGrid
    {
        get;
        set;
    }


    public bool AddHorizontalBorder
    {
        get => (bool)GetValue(AddHorizontalBorderProperty);
        set => SetValue(AddHorizontalBorderProperty, value);
    }


    public bool AddVerticalBorder
    {
        get => (bool)GetValue(AddVerticalBorderProperty);
        set => SetValue(AddVerticalBorderProperty, value);
    }


    public bool AddAroundBorder
    {
        get => (bool)GetValue(AddAroundBorderProperty);
        set => SetValue(AddAroundBorderProperty, value);
    }


    public BorderPosition BorderPosition { get; set; } = BorderPosition.None;


    public bool IsBorderVisible { get; set; } = true;


    public BorderGridHideOrientation HideOrientation
    {
        get;
        set;
    }


    public BorderStyle BorderStyle
    {
        get => (BorderStyle)GetValue(BorderStyleProperty);
        set => SetValue(BorderStyleProperty, value);
    }


    public Brush BorderBrush
    {
        get => (Brush)GetValue(BorderBrushProperty);
        set => SetValue(BorderBrushProperty, value);
    }


    public Color BorderColor
    {
        get => (Color)GetValue(BorderColorProperty);
        set => SetValue(BorderColorProperty, value);
    }


    public bool IsMerged
    {
        get => (bool)GetValue(IsMergedProperty);
        set => SetValue(IsMergedProperty, value);
    }


    public bool IsHidden
    {
        get => (bool)GetValue(IsHiddenProperty);
        set => SetValue(IsHiddenProperty, value);
    }

    public override string ToString() => $"<{Row}, {Column}> : <{Row + RowSpan - 1}, {Column + ColumnSpan - 1}>";


    public bool IsContained([NotNullWhen(true)] BorderGridCell? cell)
    {
        return cell != null
               && cell.Row >= Row && cell.Row < Row + RowSpan
               && cell.Column >= Column && cell.Column < Column + ColumnSpan;
    }


    public bool IsDirectlyContained([NotNullWhen(true)] BorderGridCell? cell) => cell?.ContainingRange == this;


    internal static void InitializeHiddenAndMergedProperty()
    {
        lock (Lock)
        {
            foreach (BorderGridRange range in IsHiddenNeedToSet.Where(r => r.IsInitialized).ToArray())
            {
                ChangedVisibility(range, true);
                IsHiddenNeedToSet.Remove(range);
            }

            foreach (BorderGridRange range in IsMergedNeedToSet.Where(r => r.IsInitialized).ToArray())
            {
                MergeRange(range, true);
                IsMergedNeedToSet.Remove(range);
            }
        }
    }


    public bool IsSubRangeOrThis(BorderGridRange? range)
    {
        if (range == null || range.Level < Level)
        {
            return false;
        }

        BorderGridRange? current = range;

        while (current != null)
        {
            if (current == this || _childRanges.Contains(current))
            {
                return true;
            }

            current = current.ContainingRange;
        }

        return false;
    }


    internal bool IsLeftBoundCell([NotNullWhen(true)] BorderGridCell? cell) =>
        IsContained(cell) && cell.Column <= _visibleBounds[X1];


    internal bool IsTopBoundCell([NotNullWhen(true)] BorderGridCell? cell) =>
        IsContained(cell) && cell.Row <= _visibleBounds[Y1];


    internal bool IsRightBoundCell([NotNullWhen(true)] BorderGridCell? cell) =>
        IsContained(cell) && cell.Column >= _visibleBounds[X2];


    internal bool IsBottomBoundCell([NotNullWhen(true)] BorderGridCell? cell) =>
        IsContained(cell) && cell.Row >= _visibleBounds[Y2];


    internal bool IsBoundCell([NotNullWhen(true)] BorderGridCell? cell, int boundIndex)
    {
        if (!IsContained(cell))
        {
            return false;
        }

        switch (boundIndex)
        {
            case X1:
                return cell.Column <= _visibleBounds[boundIndex];

            case X2:
                return cell.Column >= _visibleBounds[boundIndex];

            case Y1:
                return cell.Row <= _visibleBounds[boundIndex];

            case Y2:
                return cell.Row >= _visibleBounds[boundIndex];
        }

        return false;
    }


    internal bool IsInnerCell([NotNullWhen(true)] BorderGridCell? cell)
    {
        return IsContained(cell) &&
               cell.Row > _visibleBounds[Y1] && cell.Row < _visibleBounds[Y2] &&
               cell.Column > _visibleBounds[X1] && cell.Column < _visibleBounds[X2];
    }


    internal void UpdateVisibleBound(int index, int? bound)
    {
        if (bound is null || bound < 0)
        {
            return;
        }

        _visibleBounds[index] = (int)bound;
    }


    private void UpdateBorder()
    {
        // 查找更新边框的边界。因为非右、下边界单元格只添加左、上侧边框，因此，封闭区间的右、下侧边框需要外侧单元格添加。
        // 此处，需要查找对应方向的第一个非隐藏的行、列，直到最外侧的行、列。
        BorderGridCell? cell;
        int endRow = Row + RowSpan;
        // 隐藏单元格时，是成行、列隐藏的，查找任意一个单元格即可
        cell = OwnerGrid?.Ranges.GetCell(endRow - 1, 0)?.GetNextBottomVisibleCell();
        if (cell != null)
        {
            endRow = cell.Row + 1;
        }

        int endCol = Column + ColumnSpan + 1;
        cell = OwnerGrid?.Ranges.GetCell(0, endCol)?.GetNextRightVisibleCell();
        if (cell != null)
        {
            endCol = cell.Column + 1;
        }

        for (int r = Row; r < Math.Min(endRow, OwnerGrid!.Rows); r++)
        {
            for (int c = Column; c < Math.Min(endCol, OwnerGrid!.Columns); c++)
            {
                OwnerGrid.Ranges.GetCell(r, c)?.UpdateBorder(false);
            }
        }
    }


    private static void OnAddHorizontalBorderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        BorderGridRange range = (BorderGridRange)d;

        if (e.NewValue is true)
        {
            range.BorderPosition |= BorderPosition.Horizontal;
        }
        else
        {
            range.BorderPosition &= ~BorderPosition.Horizontal;
        }

        if (!range.IsInitialized)
        {
            return;
        }

        range.UpdateBorder();
    }


    private static void OnAddVerticalBorderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        BorderGridRange range = (BorderGridRange)d;

        if (e.NewValue is true)
        {
            range.BorderPosition |= BorderPosition.Vertical;
        }
        else
        {
            range.BorderPosition &= ~BorderPosition.Vertical;
        }

        if (!range.IsInitialized)
        {
            return;
        }

        range.UpdateBorder();
    }


    private static void OnAddAroundBorderPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        BorderGridRange range = (BorderGridRange)d;

        if (e.NewValue is true)
        {
            range.BorderPosition |= BorderPosition.Around;
        }
        else
        {
            range.BorderPosition &= ~BorderPosition.Around;
        }

        if (!range.IsInitialized)
        {
            return;
        }

        range.UpdateBorder();
    }


    private static void OnBorderStylePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        BorderGridRange range = (BorderGridRange)d;

        if (!range.IsInitialized)
        {
            return;
        }

        range.UpdateBorder();
    }


    private static void OnBorderColorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        BorderGridRange range = (BorderGridRange)d;
        range.BorderBrush = new SolidColorBrush((Color)e.NewValue);
    }


    private bool IsAnyContainingRangeMerged()
    {
        BorderGridRange? range = ContainingRange;

        while (range != null)
        {
            if (range.IsMerged)
            {
                return true;
            }

            range = range.ContainingRange;
        }

        return false;
    }


    internal void RefreshMergedFlags(bool isMerged, bool deep)
    {
        int startRow = Row;
        int endRow = Row + RowSpan;
        int startColumn = Column;
        int endColumn = Column + ColumnSpan;

        for (int r = startRow; r < endRow; r++)
        {
            for (int c = startColumn; c < endColumn; c++)
            {
                BorderGridCell? cell = OwnerGrid?.Ranges.GetCell(r, c);

                if (IsTopBoundCell(cell))
                {
                    cell.AddOrClearFlags(isMerged, BorderCellFlags.MergedRangeTopBoundCell);
                }

                if (IsBottomBoundCell(cell))
                {
                    cell.AddOrClearFlags(isMerged, BorderCellFlags.MergedRangeBottomBoundCell);
                }

                if (IsLeftBoundCell(cell))
                {
                    cell.AddOrClearFlags(isMerged, BorderCellFlags.MergedRangeLeftBoundCell);
                }

                if (IsRightBoundCell(cell))
                {
                    cell.AddOrClearFlags(isMerged, BorderCellFlags.MergedRangeRightBoundCell);
                }

                if (IsInnerCell(cell))
                {
                    cell.AddOrClearFlags(isMerged, BorderCellFlags.MergedRangeInnerCell);
                }
            }
        }

        if (deep)
        {
            foreach (BorderGridRange subRange in _childRanges)
            {
                subRange.RefreshMergedFlags(isMerged, true);
            }
        }
    }


    public static void MergeRange(BorderGridRange range, bool isMerged)
    {
        // 如果有任意一个外部区间处于合并状态，不需要更新
        if (!range.IsInitialized || range.IsAnyContainingRangeMerged())
        {
            return;
        }

        range.RefreshMergedFlags(isMerged, false);
        range.UpdateBorder();
    }


    private static void OnIsMergedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        BorderGridRange range = (BorderGridRange)d;

        if (!range.IsInitialized)
        {
            if (range.IsMerged)
            {
                IsMergedNeedToSet.Add(range);
            }

            return;
        }

        MergeRange(range, (bool)e.NewValue);
    }


    private static void OnIsHiddenPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        BorderGridRange range = (BorderGridRange)d;

        if (!range.IsInitialized)
        {
            if (range.IsHidden)
            {
                IsHiddenNeedToSet.Add(range);
            }

            return;
        }

        bool isHidden = (bool)e.NewValue;
        ChangedVisibility(range, isHidden);
    }

    internal static void ChangedVisibility(BorderGridRange range, bool isHidden)
    {
        if (range.OwnerGrid?.IsLoaded != true)
        {
            return;
        }

        range.HideCells(isHidden);

        if (range.HideOrientation == BorderGridHideOrientation.Row)
        {
            int row = range.Row;
            int rowSpan = range.RowSpan;
            for (int r = row; r < row + rowSpan; r++)
            {
                range.OwnerGrid?.ChangeRowVisibility(row, isHidden);
            }
        }
        else
        {
            int col = range.Column;
            int colSpan = range.ColumnSpan;
            for (int c = col; c < col + colSpan; c++)
            {
                range.OwnerGrid?.ChangeColumnVisiblity(col, isHidden);
            }
        }

        range.OwnerGrid?.UpdateLayout();
        range.OwnerGrid?.Ranges.UpdateBorder();
    }


    private void HideCells(bool hide)
    {
        if (OwnerGrid == null)
        {
            return;
        }

        int startRow = Row;
        int endRow = Row + RowSpan;
        int startColumn = Column;
        int endColumn = Column + ColumnSpan;

        if (HideOrientation == BorderGridHideOrientation.Row)
        {
            startColumn = 0;
            endColumn = OwnerGrid.Columns;
        }
        else
        {
            startRow = 0;
            endRow = OwnerGrid.Rows;
        }

        for (int r = startRow; r < endRow; r++)
        {
            for (int c = startColumn; c < endColumn; c++)
            {
                BorderGridCell? cell = OwnerGrid.Ranges.GetCell(r, c);
                if (cell != null)
                {
                    cell.IsHidden = hide;
                }
            }
        }
    }

    #region IList

    private readonly List<BorderGridRange> _childRanges = [];

    public bool IsReadOnly => false;

    public int Count => _childRanges.Count;

    public object SyncRoot => throw new NotSupportedException();

    public BorderGridRange this[int index]
    {
        get => _childRanges[index];
        set
        {
            _childRanges[index] = value;
            value.ContainingRange = this;
            value.OwnerGrid = OwnerGrid;
        }
    }


    public void Add(BorderGridRange value)
    {
        if (value.Row < Row || value.Row + value.RowSpan > Row + RowSpan ||
            value.Column < Column || value.Column + value.ColumnSpan > Column + ColumnSpan)
        {
            throw new Exception("BorderGridRange out of range.");
        }

        value.OwnerGrid = OwnerGrid;
        value.ContainingRange = this;
        _childRanges.Add(value);
    }


    public void Clear() => _childRanges.Clear();


    public bool Contains(BorderGridRange value) => _childRanges.Contains(value);


    public int IndexOf(BorderGridRange value) => _childRanges.IndexOf(value);


    public void Insert(int index, BorderGridRange value) => _childRanges.Insert(index, value);


    public bool Remove(BorderGridRange value) => _childRanges.Remove(value);


    public void RemoveAt(int index) => _childRanges.RemoveAt(index);


    public void CopyTo(BorderGridRange[] array, int index) => _childRanges.CopyTo(array, index);


    public IEnumerator GetEnumerator() => _childRanges.GetEnumerator();

    IEnumerator<BorderGridRange> IEnumerable<BorderGridRange>.GetEnumerator() => _childRanges.GetEnumerator();

    #endregion
}